Methods

Spatial data preparation

Load data

Maps data

We use three different shapefiles for the continental U.S. land mass, the State waters of maine, new hampshire, massachusets, connecticut, rhode island, new york, new jersey, delaware, maryland, virginia, north carolina and the North East EEZ.

1.- Land shapefile; covers the US land territory for visualization. Data provided from the map package.

2.- State waters; covers the state waters of the NE US states. Data from data.gov.

License: No license information was provided. If this work was prepared by an officer or employee of the United States government as part of that person’s official duties it is considered a U.S. Government Work.

3.- EEZ shapefile; Used the Sea Around Us shapefle updated to June 2016.

unique(land_sf$ID)

Create base shapefile for computation

As a first step we need to divide the NE US EEZ among the different states. For that we expanded state waters up to the 200 nautical miles to then estimate the percentage that each expanded-state-waters occupied. Note that in all cases these areas overlapped and percentages accounted for it. We did this by following these steps:

    1. Expand state waters using a buffer
    1. Grid that buffer on a 0.3 resolution
    1. Crop the buffer to the EEZ

1. Expand Spatial regions (a.k.a buffers)

We set a buffer of 410000 m (410 km, ~ 221 nm) that overshoots the EEZ a bit, but is eventually cropped

2. Expand grid within buffer

Here we expand a grid within the buffer so we can estimate the proportion of each state.

Note: Dark grey shaded area is the grid

2.1 Merge grid and buffers

Once we have a gridded area, we converted the grid to a sf so we can merge it with the buffered states and finally filter out everything outside the states polygon

3. Crop buffers to EEZ

Finally, we crop the grided buffers to within the EEZ to capture the actual water space.

Note: This step takes quite a while because of the size of the EEZ shapefile. No, you cannot use st_simplify() here



# I don't know how to undo st_simplify so need to re-load the shapefile
eez_sf <- st_read(dsn = path_world,
                        layer =file_path_sans_ext(fnam_world)) %>% 
  rename(eez_name = Name) %>% 
  st_transform(crs = 4326) %>% # 4326
  filter(eez_name == "USA (East Coast)")

# Get the overlapping segments
grid_eez_sf <- st_intersection(grid_sf,eez_sf)

# write_sf(grid_eez_sf, paste0(my_path("D", "Spatial"),"grid_eez_sf.shp"))

# Get final df with index
grid_eez_df <- as.data.frame(grid_eez_sf) %>%
  select(state,index) %>% 
  left_join(ne_grid, 
            by = "index")

# write_csv(grid_eez_df, paste0(my_path("R", "Partial"),"grid_eez_df.csv"))

# Plot to make sure makes sense (Picasso syle)
grid_eez_sf %>%
  st_simplify(preserveTopology = TRUE, dTolerance = 10000) %>% #0.1 for paper
  ggplot() +
  geom_sf(aes(color = state), alpha = 0.3)

Interpolation rutine

In this step we interpolate the survey data within the previously created grid following a Triangular Irregular Surface method.

  • We removed cases where wtcpue < 0

Functions needed

We need to create a couple of functions to run the whole process

Interpolation main fx (tis).

This is the main function used to interpolate the data per year. It follows a Triangular Irregular Surface method using the interp::interp() function. If you want to see the function clic on code


tis <- function(input_data, grid, yr, taxa, reg){
  
  # --------------- #
  # Testing
  # print(paste(yr))
  # yr = 1976
  # --------------- #
  
  # Filter data
  data <- input_data %>% 
    filter(year == yr,
           spp == taxa,
           region == reg
    ) %>% 
    # Average duplicated hauls in the same spot
    group_by(region,year,spp,lat,lon) %>%
    summarise_at(vars(wtcpue),
                 mean,na.rm = T) %>%
    filter(wtcpue > 0)
    
  # Only interpolate cases where there is more than 3 rows
  # Marked by the function 
    if(nrow(data) <= 3){
      fit_tin <- tibble()
    }else{
      
      # Triangular Irregular Surface
      fit_tin <- interp::interp( # using {interp}
        x = data$lon,           # the function actually accepts coordinate vectors
        y = data$lat,
        z = data$wtcpue,
        xo = grid$lon,     # here we already define the target grid
        yo = grid$lat,
        output = "points"
      ) %>% 
        bind_cols() %>% 
        bind_cols(grid) %>%
        mutate(year = yr,
               region = reg,
               spp = taxa) %>% 
        select(index, state, year, region, spp, lon=x, lat=y, value = z)
      
    }
   
    return(fit_tin)
}

# Test me
# Needs variables in Control panel
# Test no data: "Alosa aestivalis", reg = "Northeast US Fall", yr = 1974 
# tis(input_data = ocean_data, grid = grid_eez_df, taxa = "Illex illecebrosus", reg = "Northeast US Fall", yr = 1973)



# lapply(years,tis,input_data = ocean_data, grid = grid_eez_df, taxa = "Illex illecebrosus", reg = regions[2])
Run function

This is a sub-function that runs the tis() function by taxa and region. It saves the output as a .csv file for each species.



run_tis <- function(input_data, grid, years, taxa, reg){
  
  
  # Run tis for species and surveys
  for(r in 1:2){
    
    partial_df <- bind_rows(
      lapply(years,tis,input_data = ocean_data, grid = grid_eez_df, taxa = taxa, reg = regions[r])
    )
    
    if(r == 1){
      historic_tif <- partial_df
    }else{
      historic_tif <- bind_rows(historic_tif,partial_df)
    }
    
  }
  
  # ----------------------- #
  # Save dataset per species
  # ----------------------- #
  
  # Set file name
  name <- paste0("tif_",str_replace(taxa," ","_"),".csv")
  
  # Set path name
  save_path <- my_path("R","Partial/Interpolation")
  
  # Set complete path
  save_name <- paste0(save_path,name)
  
  # Create folder if it does not exist
  if(file.exists(save_path) == F){
    dir.create(save_path)
  }
  
  #  Save file
  write_csv(historic_tif,save_name)
  
  return_msg <- paste("Interpolation done for", taxa)
  
  return(return_msg)
  
  
}

# Test me
    # run_tis(input_data = ocean_data, grid = grid_eez_df, taxa = "Centropristis striata", years = years, reg = regions)

Survey data

The interpolation was done with NOAA Northeast Fisheries Science Center Spring and Fall Bottom Trawl Surveys data provided by Ocean adapt. Data was accessed trough the Github.

In primary publications using data from the database, please cite Pinsky et al. 2013. Marine taxa track local climate velocities. Science 341: 1239-1242 doi: 10.1126/science.1239352, as well as the original data sources.

  • Using only the Northeast US Fall and Spring bottom trawl survey data for now
Splitting up data
  • No part on spatial analysis. Can be ignored.

This is just a sub-step to split up the data into single species files. This makes the app faster as it only needs to load species specific data, rather than all the data at de begining.

Control Pannel

This is where we load the required data and prepare to run the interpolation function. Note that some of the data here has been previously created


# Load grid df
grid_eez_df <- my_path("D","Spatial","grid_eez_df.csv", read = T)

# Run interpolation for all years
years <- seq(1973,2019,1)

# regions
regions <- c("Northeast US Fall" , "Northeast US Spring")

# species list
spp <- ocean_data %>% 
  filter(region %in% regions,
         spp != "NA") %>% 
  pull(spp) %>% 
  unique()


# Double check runs

spp_runs <- tibble(taxa = (list.files(my_path("R","Partial/Interpolation")))) %>%
  mutate(
    taxa = str_remove(taxa, "tif_"),
    taxa = str_remove(taxa, ".csv"),
    taxa = str_replace(taxa, "_", " ")
  ) 

# Missing runs
spp <- tibble(taxa=spp) %>% 
  anti_join(spp_runs) %>% 
  pull(taxa)

Run routine

Here we just run the routine for each of the species present in the Northeast US Fall and Spring surveys between 1973 and 2019.

  • Note there are 43 identified taxa
  • Some taxa do not have presence data in some years

# single species run
# run_tis(input_data = ocean_data,
#         grid = grid_eez_df,
#         years = years,
#         reg = regions,
#         taxa = "Illex illecebrosus"
# )


# Run them all in parallel
parallel::mclapply(spp,
                   run_tis, 
                   input_data = ocean_data,
                   grid = grid_eez_df,
                   years = years,
                   reg = regions
)

Results

This section shows preliminary results for Black sea bass (Centropristis striata).


# Load interpolated data
# "Centropristis striata"
# 
historic_tif <- my_path("R","Partial/Interpolation","tif_Centropristis_striata.csv", read = T)

# Spatial data

States <- c("maine", "new hampshire", "massachusetts", "connecticut", "rhode island", "new york", "new jersey", "delaware", "maryland", "virginia", "north carolina","pennsylvania") 

# US State Map (land)

land_sf <- st_as_sf(map("state", plot = FALSE, fill = TRUE)) %>% 
  filter(ID %in% States) 


# Periods
periods <-tibble(
  period = c(rep("a early",12),
             rep("b mid",12),
             rep("c late",12),
             rep("d now",12)
             ),
  year = c(seq(1972,1984,1),
            seq(1985,1997,1),
            seq(1998,2011,1),
            seq(2012,2019,1)
            )
  )

# State pallet

state_pallet <- c(wes_palette(n = 5, name = "Darjeeling1"),
                               wes_palette(n = 5, name = "Darjeeling2"),
                               wes_palette(n = 3, name = "Royal1")
                  )



# print for notebook
head(historic_tif)

Average proportion

This map shows the average proportion of the extrapolated value from the whole study period within each State’s water.

Note: Grey tiles represent NAs

Proportion Change

Here we show the average proportion of the interpolation by State and time period. Time periods were arbitrary defined as;

  • Early; 1972 to 1984
  • Mid; 1985 to 1997
  • Late; 1998 to 2011
  • Now; 2012 to 2019

Notes: To panel represents Fall survey and bottom panel Spring. This computation considers the Overlapping of state waters.


total_fited <- historic_tif %>% 
  group_by(year,region) %>% 
  summarise(total_value = sum(value,na.rm=T),.groups = "drop")

state_fit <- historic_tif %>% 
  group_by(state,year,region) %>% 
  summarise(state_value = sum(value,na.rm= T), .groups = "drop") %>% 
  left_join(total_fited,
            by = c("year","region")) %>%
  mutate(percentage = state_value/total_value*100) %>% 
  left_join(periods) %>% 
  group_by(ID = state,period,region) %>% 
  summarise(mean_per = round(mean(percentage)),.groups = "drop")
Joining, by = "year"
  
# check percentages
# state_fit %>% 
#   group_by(period) %>% 
#   summarise(sum(mean_per))


land_sf %>% 
  left_join(state_fit,
            by = "ID") %>% 
  ggplot() +
  geom_sf(aes(fill = mean_per)) +
  viridis::scale_fill_viridis("Mean Proportion", alpha = 0.8) +
  facet_wrap(~region + period, nrow = 2) +
  my_ggtheme_m()

Area plot

This graph shows the proportion of the interpolation value each State has over time.

Note: This plot is interactive. For ease comparison between States you can;

  • One click on any State to remove it from the plot
  • Two clicks on any State to isolate it from the plot (other states can then be added by just clicking on them).
  • The bottom panel allows you to reduce the time frame

total_fited <- historic_tif %>% 
  group_by(year,region) %>% 
  summarise(total_value = sum(value,na.rm=T),.groups = "drop")

# group by state
state_fit <- historic_tif %>% 
  group_by(state,year,region) %>% 
  summarise(state_value = sum(value,na.rm= T), .groups = "drop") %>% 
  left_join(total_fited,
            by = c("year","region")) %>%
  mutate(percentage = state_value/total_value*100)

p <- ggplot(state_fit) +
  geom_area(
    aes(
      x = year,
      y = round(percentage),
      fill = state
    )
  ) +
  ylab("Percentage (%)") +
  # viridis::scale_fill_viridis(discrete = T, alpha = 0.8) +
  scale_fill_manual(values = state_pallet) +
  MyFunctions::my_ggtheme_p() +
  facet_wrap(~region, ncol = 1)

ggplotly(p,
         dynamicTicks = TRUE) %>% 
  layout(hovermode = "x") %>%
  rangeslider()
NA

Area plot (running mean)

This graph shows the proportion of each State smoothed over a *10 years running mean**. It allows to better see increasing and decreasing trends.

Note: This plot is also interactive and thus, follows the same options of the previous one.


# group by state
state_fit <- historic_tif %>% 
  group_by(state,year,region,.groups = "drop") %>% 
  summarise(state_value = sum(value,na.rm= T), .groups = "drop") %>% 
  left_join(total_fited,
            by = c("year","region")) %>%
  mutate(percentage = state_value/total_value*100) %>% 
  group_by(state,region) %>% 
  mutate(RMean = zoo::rollmean(x = percentage, 
                            10,
                            fill = NA,
                            na.rm=T)
    )

# Plot me
p <- ggplot(state_fit) +
  geom_area(
    aes(
      x = year,
      y = round(RMean),
      fill = state
    )
  ) +
  ylab("10 yrs running average (%)") +
  # viridis::scale_fill_viridis(discrete = T, alpha = 0.8) +
  scale_fill_manual(values = state_pallet) +
  MyFunctions::my_ggtheme_p() +
  facet_wrap(~region, ncol = 1)

suppressWarnings(
ggplotly(p,
         dynamicTicks = TRUE) %>% 
  layout(hovermode = "x") %>% 
  # add_trace() %>% 
  rangeslider()
)
NA
LS0tCnRpdGxlOiAiU3BhdGlhbCBBbmFseXNpcyIKYXV0aG9yOiAiSnVsaWFubyBQYWxhY2lvcyBBYnJhbnRlcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfd2lkdGg6IDYKICAgIGZpZ19oZWlnaHQ6IDQKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZGFya2x5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoTXlGdW5jdGlvbnMpCk15RnVuY3Rpb25zOjpteV9saWIoYygiZ2dtYXAiLCJzZiIsInRpZHl2ZXJzZSIsInRvb2xzIiwicmVhZHIiLCJkYXRhLnRhYmxlIiwibWFwcyIsInZpcmlkaXMiLCJ3ZXNhbmRlcnNvbiIsImtuaXRyIiwia2FibGVFeHRyYSIsInBsb3RseSIpKQpgYGAKCiMgTWV0aG9kcwoKIyMgU3BhdGlhbCBkYXRhIHByZXBhcmF0aW9uCgojIyMgTG9hZCBkYXRhCgojIyMjIE1hcHMgZGF0YQoKV2UgdXNlIHRocmVlIGRpZmZlcmVudCBzaGFwZWZpbGVzIGZvciB0aGUgY29udGluZW50YWwgVS5TLiBsYW5kIG1hc3MsIHRoZSBTdGF0ZSB3YXRlcnMgb2YgbWFpbmUsIG5ldyBoYW1wc2hpcmUsIG1hc3NhY2h1c2V0cywgY29ubmVjdGljdXQsIHJob2RlIGlzbGFuZCwgbmV3IHlvcmssIG5ldyBqZXJzZXksIGRlbGF3YXJlLCBtYXJ5bGFuZCwgdmlyZ2luaWEsIG5vcnRoIGNhcm9saW5hIGFuZCB0aGUgTm9ydGggRWFzdCBFRVouCgoxLi0gKipMYW5kIHNoYXBlZmlsZTsqKiBjb3ZlcnMgdGhlIFVTIGxhbmQgdGVycml0b3J5IGZvciB2aXN1YWxpemF0aW9uLiBEYXRhIHByb3ZpZGVkIGZyb20gdGhlIGBtYXBgIHBhY2thZ2UuCgoyLi0gKipTdGF0ZSB3YXRlcnM7KiogY292ZXJzIHRoZSBzdGF0ZSB3YXRlcnMgb2YgdGhlIE5FIFVTIHN0YXRlcy4gRGF0YSBmcm9tIFtkYXRhLmdvdl0oaHR0cHM6Ly9jYXRhbG9nLmRhdGEuZ292L2RhdGFzZXQvZmVkZXJhbC1hbmQtc3RhdGUtd2F0ZXJzKS4gIAoKKkxpY2Vuc2U6IE5vIGxpY2Vuc2UgaW5mb3JtYXRpb24gd2FzIHByb3ZpZGVkLiBJZiB0aGlzIHdvcmsgd2FzIHByZXBhcmVkIGJ5IGFuIG9mZmljZXIgb3IgZW1wbG95ZWUgb2YgdGhlIFVuaXRlZCBTdGF0ZXMgZ292ZXJubWVudCBhcyBwYXJ0IG9mIHRoYXQgcGVyc29uJ3Mgb2ZmaWNpYWwgZHV0aWVzIGl0IGlzIGNvbnNpZGVyZWQgYSBVLlMuIEdvdmVybm1lbnQgV29yay4qCgozLi0gKipFRVogc2hhcGVmaWxlOyoqIFVzZWQgdGhlICpTZWEgQXJvdW5kIFVzKiBzaGFwZWZsZSB1cGRhdGVkIHRvIEp1bmUgMjAxNi4KCmBgYHtyIG1hcHNfc2YsIGV2YWwgPSBULCBlY2hvID0gVCwgcmVzdWx0cyA9ICdoaWRlJ30KClN0YXRlcyA8LSBjKCJtYWluZSIsICJuZXcgaGFtcHNoaXJlIiwgIm1hc3NhY2h1c2V0dHMiLCAiY29ubmVjdGljdXQiLCAicmhvZGUgaXNsYW5kIiwgIm5ldyB5b3JrIiwgIm5ldyBqZXJzZXkiLCAiZGVsYXdhcmUiLCAibWFyeWxhbmQiLCAidmlyZ2luaWEiLCAibm9ydGggY2Fyb2xpbmEiLCJwZW5uc3lsdmFuaWEiKSAKCiMgVVMgU3RhdGUgTWFwIChsYW5kKQoKbGFuZF9zZiA8LSBzdF9hc19zZihtYXAoInN0YXRlIiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpICU+JSAKICBmaWx0ZXIoSUQgJWluJSBTdGF0ZXMpIAoKIyBnZ3Bsb3QodXNfbWFwKSArCiAgIyBnZW9tX3NmKCkKCiMgVVMgRUVaIG1hcAoKIyBwYXRoX3dvcmxkIDwtIE15RnVuY3Rpb25zOjpteV9wYXRoKCJHIiwgZXh0cmFfcGF0aCA9ICJTcGF0aWFsL1NBVS9TQVVfU2hhcGVmaWxlIikKcGF0aF93b3JsZCA8LSAifi9Eb3dubG9hZHMvU0FVX1NoYXBlZmlsZSIKCiMgVGhlIEZpbGUKZm5hbV93b3JsZCA8LSAiU0FVRUVaX0p1bHkyMDE1LnNocCIKCiMgTG9hZCBpdCEKZWV6X3NmIDwtIHN0X3JlYWQoZHNuID0gcGF0aF93b3JsZCwKICAgICAgICAgICAgICAgICAgICAgICAgbGF5ZXIgPWZpbGVfcGF0aF9zYW5zX2V4dChmbmFtX3dvcmxkKSkgJT4lIAogIHJlbmFtZShlZXpfbmFtZSA9IE5hbWUpICU+JSAKICBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNikgJT4lICMgNDMyNgogIHN0X3NpbXBsaWZ5KHByZXNlcnZlVG9wb2xvZ3kgPSBUUlVFLCBkVG9sZXJhbmNlID0gMTAwMDApICU+JSAjMC4xIGZvciBwYXBlcgogIGZpbHRlcihlZXpfbmFtZSA9PSAiVVNBIChFYXN0IENvYXN0KSIpCgoKIyBnZ3Bsb3QoZWV6X3NmKSArCiMgZ2VvbV9zZihhZXMoKSxmaWxsID1OQSkKCiMgZ2dwbG90KCkgKwojICAgZ2VvbV9zZihkYXRhID0gV29ybGRfZWV6X3NmLCBhZXMoKSwgY29sb3IgPSAicmVkIikgKwojICAgZ2VvbV9zZihkYXRhID0gdXNfbWFwLCBhZXMoKSwgY29sb3IgPSAiYmx1ZSIpCgojIFVTIHN0YXRlIHdhdGVycwojIGh0dHBzOi8vY2F0YWxvZy5kYXRhLmdvdi9kYXRhc2V0L2ZlZGVyYWwtYW5kLXN0YXRlLXdhdGVycwoKIyB1c19zdGF0ZV93IDwtIHJnZGFsOjpyZWFkT0dSKGRzbiA9ICJ+L0Rvd25sb2Fkcy9GZWRlcmFsQW5kU3RhdGVXYXRlcnMvRmVkZXJhbEFuZFN0YXRlV2F0ZXJzLmdkYiIpCnN0YXRlX3NmIDwtICBzdF9yZWFkKCJ+L0Rvd25sb2Fkcy9GZWRlcmFsQW5kU3RhdGVXYXRlcnMvRmVkZXJhbEFuZFN0YXRlV2F0ZXJzLmdkYiIpICU+JQogIGphbml0b3I6OmNsZWFuX25hbWVzKCkgJT4lIAogIG11dGF0ZShqdXJpc2RpY3RpID0gc3RyX3RvX2xvd2VyKGp1cmlzZGljdGkpKSAlPiUgCiAgZmlsdGVyKGp1cmlzZGljdGkgJWluJSBTdGF0ZXMpICU+JSAKICByZW5hbWUoc3RhdGUgPSBqdXJpc2RpY3RpKSAlPiUgCiAgIyBzdF90cmFuc2Zvcm0oY3JzID0gNDMyNikgICMgZm9yIG1hdGNoaW5nIHByb2plY3Rpb25zCiAgc3Rfc2ltcGxpZnkocHJlc2VydmVUb3BvbG9neSA9IFRSVUUsIGRUb2xlcmFuY2UgPSAxMDAwMCkgIzAuMSBmb3IgcGFwZXIKCgpncmlkX3NmIDwtICBzdF9yZWFkKCIvVm9sdW1lcy9FbnRlcnByaXNlL0RhdGEvQWNyb3NzQm91bmRhcmllcy9EYXRhL1NwYXRpYWwvZ3JpZF9zZi9ncmlkX2Vlel9zZi5zaHAiKQoKdW5pcXVlKGxhbmRfc2YkSUQpCgpgYGAKCgojIyMgQ3JlYXRlIGJhc2Ugc2hhcGVmaWxlIGZvciBjb21wdXRhdGlvbgoKQXMgYSBmaXJzdCBzdGVwIHdlIG5lZWQgdG8gZGl2aWRlIHRoZSBORSBVUyBFRVogYW1vbmcgdGhlIGRpZmZlcmVudCBzdGF0ZXMuIEZvciB0aGF0IHdlIGV4cGFuZGVkIHN0YXRlIHdhdGVycyB1cCB0byB0aGUgMjAwIG5hdXRpY2FsIG1pbGVzIHRvIHRoZW4gZXN0aW1hdGUgdGhlIHBlcmNlbnRhZ2UgdGhhdCBlYWNoIGV4cGFuZGVkLXN0YXRlLXdhdGVycyBvY2N1cGllZC4gTm90ZSB0aGF0IGluIGFsbCBjYXNlcyB0aGVzZSBhcmVhcyBvdmVybGFwcGVkIGFuZCBwZXJjZW50YWdlcyBhY2NvdW50ZWQgZm9yIGl0LiBXZSBkaWQgdGhpcyBieSBmb2xsb3dpbmcgdGhlc2Ugc3RlcHM6CgotIDEuIEV4cGFuZCBzdGF0ZSB3YXRlcnMgdXNpbmcgYSBidWZmZXIKCi0gMi4gR3JpZCB0aGF0IGJ1ZmZlciBvbiBhIDAuMyByZXNvbHV0aW9uCgotIDMuIENyb3AgdGhlIGJ1ZmZlciB0byB0aGUgRUVaCgojIyMjIDEuIEV4cGFuZCBTcGF0aWFsIHJlZ2lvbnMgKGEuay5hICBidWZmZXJzKQoKV2Ugc2V0IGEgYnVmZmVyIG9mICo0MTAwMDAqIG0gKDQxMCBrbSwgfiAyMjEgbm0pIHRoYXQgb3ZlcnNob290cyB0aGUgRUVaIGEgYml0LCBidXQgaXMgZXZlbnR1YWxseSBjcm9wcGVkCgpgYGB7ciBidWZmZXIsIGV2YWwgPSBULCBlY2hvID0gVH0KCiMgQnVmZmVyIHN0YXRlIHdhdGVycwpzdGF0ZV9iZiA9IHN0X2J1ZmZlcihzdGF0ZV9zZiwgNDEwMDAwKSAlPiUgCiAgc3RfdHJhbnNmb3JtKDQzMjYpICU+JSAjIHRvIG1hdGNoIHNoYXBlCiAgc3Rfc2V0X2Nycyg0MzI2KQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIwojIFRlc3RpbmcgYW5kIHZpc3VhbGl6aW5nIHRoZSBidWZmZXIgCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICMKCnN0YXRlX2JmX3Bsb3QgPC0gZ2dwbG90KCkgKwogIGdlb21fc2YoZGF0YSA9IGVlel9zZiwgYWVzKCksIGZpbGwgPSBOQSkgKwogIGdlb21fc2YoZGF0YSA9IHN0YXRlX2JmLCBhZXMoY29sb3IgPSBzdGF0ZSksIGZpbGwgPSBOQSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzdGF0ZV9wYWxsZXQpCiMgICAKCiMgZ2dzYXZlKCIuLi9SZXN1bHRzL1BhcnRpYWwvc3RhdGVfd2F0ZXJzX2J1ZmZlci5wbmciLHN0YXRlX2JmX3Bsb3QpCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSAjCgpzdGF0ZV9iZl9wbG90CgpgYGAKCiMjIyMgMi4gRXhwYW5kIGdyaWQgd2l0aGluIGJ1ZmZlcgoKSGVyZSB3ZSBleHBhbmQgYSBncmlkIHdpdGhpbiB0aGUgYnVmZmVyIHNvIHdlIGNhbiBlc3RpbWF0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIHN0YXRlLgoKKk5vdGU6KiBEYXJrIGdyZXkgc2hhZGVkIGFyZWEgaXMgdGhlIGdyaWQKCmBgYHtyIGdyaWRfaW5kZXhpbmcsIGV2YWwgPSBULCBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGfQoKIyBDcmVhdGUgZ3JpZCBvZiB0aGUgcmVnaW9uCmJib3ggPC0gYyhzdF9iYm94KHN0YXRlX2JmKSkKCiMgRXhwYW5kIHRoZSBncmlkCm5lX2dyaWQgPC0gZXhwYW5kLmdyaWQoCiAgICBsb24gPSBzZXEoZnJvbSA9IGJib3hbInhtaW4iXSwgdG8gPSBiYm94WyJ4bWF4Il0sIGJ5ID0gMC4zKSwKICAgIGxhdCA9IHNlcShmcm9tID0gYmJveFsieW1pbiJdLCB0byA9IGJib3hbInltYXgiXSwgYnkgPSAwLjMpCikgJT4lIAogIHJvd2lkX3RvX2NvbHVtbigiaW5kZXgiKQoKIyAtLS0tLS0tLS0tLSAjCiMgW1RFU1RdIFBsb3QgYWxsIGxheWVycwojIC0tLS0tLS0tLS0tICMKCiMgTG9va3MgZ29vZApzdGF0ZV9zZiAlPiUKICBzdF90cmFuc2Zvcm0oNDMyNikgJT4lICMgdG8gbWF0Y2ggc2hhcGUKICBzdF9zZXRfY3JzKDQzMjYpICU+JQogIHN0X3NpbXBsaWZ5KHByZXNlcnZlVG9wb2xvZ3kgPSBUUlVFLCBkVG9sZXJhbmNlID0gMTAwMDApICU+JQogIGdncGxvdCgpICsKICBnZW9tX3NmKGFlcyhjb2xvciA9IHN0YXRlKSkrCiAgIyBnZW9tX3NmKGRhdGEgPSBlZXpfc2YsIGFlcygpLGZpbGwgPU5BKSArCiAgIyBnZ3Bsb3QoKSsKICBnZW9tX3RpbGUoZGF0YSA9IG5lX2dyaWQsCiAgICAgICAgICAgIGFlcygKICAgICAgICAgICAgICB4ID0gbG9uLAogICAgICAgICAgICAgIHkgPSBsYXQKICAgICAgICAgICAgKSwKICAgICAgICAgICAgYWxwaGEgPSAwLjIKICApICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gc3RhdGVfcGFsbGV0KQoKCmBgYAoKIyMjIyMgMi4xIE1lcmdlIGdyaWQgYW5kIGJ1ZmZlcnMKCk9uY2Ugd2UgaGF2ZSBhIGdyaWRkZWQgYXJlYSwgd2UgY29udmVydGVkIHRoZSBncmlkIHRvIGEgYHNmYCBzbyB3ZSBjYW4gbWVyZ2UgaXQgd2l0aCB0aGUgYnVmZmVyZWQgc3RhdGVzIGFuZCBmaW5hbGx5IGZpbHRlciBvdXQgZXZlcnl0aGluZyBvdXRzaWRlIHRoZSBzdGF0ZXMgcG9seWdvbgoKYGBge3IgZ3JpZF9idWZfbWVyZ2UsIGV2YWwgPSBULCBlY2hvID0gVCwgbWVzc2FnZSA9IEYsIHdhcm5pbmcgPSBGLCBmaWcud2lkdGggPSAxMH0KCiMgLS0tLS0tLS0tLS0tLS0tLSAjCiMgQ29udmVydCBncmlkIHRvIHNmCiMgLS0tLS0tLS0tLS0tLS0tLSAjCmdyaWRfc2YgPC0gc3RfYXNfc2YobmVfZ3JpZCwKICAgICAgICAgICAgIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwKICAgICAgICAgICAgIGNycyA9IDQzMjYpICU+JSAKICBzdF9qb2luKHN0YXRlX2JmKSAlPiUgCiAgZmlsdGVyKCFpcy5uYShzdGF0ZSkpCgojIENyZWF0ZSBkYXRhIGZyYW1lIGZvciBmdXR1cmUgY29tcHV0YXRpb25zCiMgTm90ZSwgd2lsbCBiZSB1c2VkIGluIG5leHQgY2h1bmsKZ3JpZF9iZl9kdCA8LSBhcy5kYXRhLmZyYW1lKGdyaWRfc2YpICU+JQogICAgc2VsZWN0KGluZGV4LHN0YXRlKQogICAgIyBncm91cF9ieShzdGF0ZSkgJT4lIAogICAgIyBzdW1tYXJpc2UobGVuZ3RoKGluZGV4KSkKCgojIC0tLS0tLS0tLS0tLS0tLS0gIwojIFtURVNUXSAKIyBWaXN1YWxpemF0aW9uIG9mIGdyaWQKIyAtLS0tLS0tLS0tLS0tLS0tICMKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKAogICMgT3ZlcmFsbCAob3ZlcmxhcHBpbmcpIHBvc2l0aW9uCiAgZ2dwbG90KGdyaWRfc2YpICsKICAgIGdlb21fc2YoYWVzKGNvbG9yID0gc3RhdGUpLCBhbHBoYSA9IDAuMykgKwogICAgZ2VvbV9zZihkYXRhID0gZWV6X3NmLCBhZXMoKSxmaWxsID1OQSkgKyAKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzdGF0ZV9wYWxsZXQpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICIiKSwKICAjIFNob3dpbmcgZWFjaCBzdGF0ZSBzZXBhcmF0bGV5CiAgZ2dwbG90KCkgKwogICAgZ2VvbV9zZihkYXRhID0gZ3JpZF9zZiwgYWVzKGNvbG9yID0gc3RhdGUpLHNpemUgPSAwLjEsIGFscGhhID0gMC41KSArCiAgICBmYWNldF93cmFwKH5zdGF0ZSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBzdGF0ZV9wYWxsZXQpLAogIG5yb3cgPSAxKQoKYGBgCgojIyMjIDMuIENyb3AgYnVmZmVycyB0byBFRVoKCkZpbmFsbHksIHdlIGNyb3AgdGhlIGdyaWRlZCBidWZmZXJzIHRvIHdpdGhpbiB0aGUgRUVaIHRvIGNhcHR1cmUgdGhlIGFjdHVhbCB3YXRlciBzcGFjZS4KCipOb3RlOiogVGhpcyBzdGVwIHRha2VzIHF1aXRlIGEgd2hpbGUgYmVjYXVzZSBvZiB0aGUgc2l6ZSBvZiB0aGUgRUVaIHNoYXBlZmlsZS4gTm8sIHlvdSBjYW5ub3QgdXNlIGBzdF9zaW1wbGlmeSgpYCBoZXJlCgpgYGB7ciBidWZmX3RvX2VleiwgZXZhbCA9VCwgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0KCgojIEkgZG9uJ3Qga25vdyBob3cgdG8gdW5kbyBzdF9zaW1wbGlmeSBzbyBuZWVkIHRvIHJlLWxvYWQgdGhlIHNoYXBlZmlsZQplZXpfc2YgPC0gc3RfcmVhZChkc24gPSBwYXRoX3dvcmxkLAogICAgICAgICAgICAgICAgICAgICAgICBsYXllciA9ZmlsZV9wYXRoX3NhbnNfZXh0KGZuYW1fd29ybGQpKSAlPiUgCiAgcmVuYW1lKGVlel9uYW1lID0gTmFtZSkgJT4lIAogIHN0X3RyYW5zZm9ybShjcnMgPSA0MzI2KSAlPiUgIyA0MzI2CiAgZmlsdGVyKGVlel9uYW1lID09ICJVU0EgKEVhc3QgQ29hc3QpIikKCiMgR2V0IHRoZSBvdmVybGFwcGluZyBzZWdtZW50cwpncmlkX2Vlel9zZiA8LSBzdF9pbnRlcnNlY3Rpb24oZ3JpZF9zZixlZXpfc2YpCgojIHdyaXRlX3NmKGdyaWRfZWV6X3NmLCBwYXN0ZTAobXlfcGF0aCgiRCIsICJTcGF0aWFsIiksImdyaWRfZWV6X3NmLnNocCIpKQoKIyBHZXQgZmluYWwgZGYgd2l0aCBpbmRleApncmlkX2Vlel9kZiA8LSBhcy5kYXRhLmZyYW1lKGdyaWRfZWV6X3NmKSAlPiUKICBzZWxlY3Qoc3RhdGUsaW5kZXgpICU+JSAKICBsZWZ0X2pvaW4obmVfZ3JpZCwgCiAgICAgICAgICAgIGJ5ID0gImluZGV4IikKCiMgd3JpdGVfY3N2KGdyaWRfZWV6X2RmLCBwYXN0ZTAobXlfcGF0aCgiUiIsICJQYXJ0aWFsIiksImdyaWRfZWV6X2RmLmNzdiIpKQoKIyBQbG90IHRvIG1ha2Ugc3VyZSBtYWtlcyBzZW5zZSAoUGljYXNzbyBzeWxlKQpncmlkX2Vlel9zZiAlPiUKICBzdF9zaW1wbGlmeShwcmVzZXJ2ZVRvcG9sb2d5ID0gVFJVRSwgZFRvbGVyYW5jZSA9IDEwMDAwKSAlPiUgIzAuMSBmb3IgcGFwZXIKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoY29sb3IgPSBzdGF0ZSksIGFscGhhID0gMC4zKQoKYGBgCgojIyMgSW50ZXJwb2xhdGlvbiBydXRpbmUKCkluIHRoaXMgc3RlcCB3ZSBbaW50ZXJwb2xhdGVdKGh0dHBzOi8vc3dpbGtlLWdlb3NjaWVuY2UubmV0L3Bvc3Qvc3BhdGlhbF9pbnRlcnBvbGF0aW9uLykgdGhlIHN1cnZleSBkYXRhIHdpdGhpbiB0aGUgcHJldmlvdXNseSBjcmVhdGVkIGdyaWQgZm9sbG93aW5nIGEgVHJpYW5ndWxhciBJcnJlZ3VsYXIgU3VyZmFjZSBtZXRob2QuCgotIFdlIHJlbW92ZWQgY2FzZXMgd2hlcmUgYHd0Y3B1ZSA8IDBgCgojIyMjIEZ1bmN0aW9ucyBuZWVkZWQKCldlIG5lZWQgdG8gY3JlYXRlIGEgY291cGxlIG9mIGZ1bmN0aW9ucyB0byBydW4gdGhlIHdob2xlIHByb2Nlc3MKCiMjIyMjIEludGVycG9sYXRpb24gbWFpbiBmeCAodGlzKS4KClRoaXMgaXMgdGhlIG1haW4gZnVuY3Rpb24gdXNlZCB0byBpbnRlcnBvbGF0ZSB0aGUgZGF0YSBwZXIgeWVhci4gSXQgZm9sbG93cyBhIFRyaWFuZ3VsYXIgSXJyZWd1bGFyIFN1cmZhY2UgbWV0aG9kIHVzaW5nIHRoZSBgaW50ZXJwOjppbnRlcnAoKWAgZnVuY3Rpb24uIElmIHlvdSB3YW50IHRvIHNlZSB0aGUgZnVuY3Rpb24gY2xpYyBvbiBgY29kZWAKCmBgYHtyIGludGVycG9sX2Z1bmN0aW9uLCBldmFsID0gVCwgZWNobyA9IFR9Cgp0aXMgPC0gZnVuY3Rpb24oaW5wdXRfZGF0YSwgZ3JpZCwgeXIsIHRheGEsIHJlZyl7CiAgCiAgIyAtLS0tLS0tLS0tLS0tLS0gIwogICMgVGVzdGluZwogICMgcHJpbnQocGFzdGUoeXIpKQogICMgeXIgPSAxOTc2CiAgIyAtLS0tLS0tLS0tLS0tLS0gIwogIAogICMgRmlsdGVyIGRhdGEKICBkYXRhIDwtIGlucHV0X2RhdGEgJT4lIAogICAgZmlsdGVyKHllYXIgPT0geXIsCiAgICAgICAgICAgc3BwID09IHRheGEsCiAgICAgICAgICAgcmVnaW9uID09IHJlZwogICAgKSAlPiUgCiAgICAjIEF2ZXJhZ2UgZHVwbGljYXRlZCBoYXVscyBpbiB0aGUgc2FtZSBzcG90CiAgICBncm91cF9ieShyZWdpb24seWVhcixzcHAsbGF0LGxvbikgJT4lCiAgICBzdW1tYXJpc2VfYXQodmFycyh3dGNwdWUpLAogICAgICAgICAgICAgICAgIG1lYW4sbmEucm0gPSBUKSAlPiUKICAgIGZpbHRlcih3dGNwdWUgPiAwKQogICAgCiAgIyBPbmx5IGludGVycG9sYXRlIGNhc2VzIHdoZXJlIHRoZXJlIGlzIG1vcmUgdGhhbiAzIHJvd3MKICAjIE1hcmtlZCBieSB0aGUgZnVuY3Rpb24gCiAgICBpZihucm93KGRhdGEpIDw9IDMpewogICAgICBmaXRfdGluIDwtIHRpYmJsZSgpCiAgICB9ZWxzZXsKICAgICAgCiAgICAgICMgVHJpYW5ndWxhciBJcnJlZ3VsYXIgU3VyZmFjZQogICAgICBmaXRfdGluIDwtIGludGVycDo6aW50ZXJwKCAjIHVzaW5nIHtpbnRlcnB9CiAgICAgICAgeCA9IGRhdGEkbG9uLCAgICAgICAgICAgIyB0aGUgZnVuY3Rpb24gYWN0dWFsbHkgYWNjZXB0cyBjb29yZGluYXRlIHZlY3RvcnMKICAgICAgICB5ID0gZGF0YSRsYXQsCiAgICAgICAgeiA9IGRhdGEkd3RjcHVlLAogICAgICAgIHhvID0gZ3JpZCRsb24sICAgICAjIGhlcmUgd2UgYWxyZWFkeSBkZWZpbmUgdGhlIHRhcmdldCBncmlkCiAgICAgICAgeW8gPSBncmlkJGxhdCwKICAgICAgICBvdXRwdXQgPSAicG9pbnRzIgogICAgICApICU+JSAKICAgICAgICBiaW5kX2NvbHMoKSAlPiUgCiAgICAgICAgYmluZF9jb2xzKGdyaWQpICU+JQogICAgICAgIG11dGF0ZSh5ZWFyID0geXIsCiAgICAgICAgICAgICAgIHJlZ2lvbiA9IHJlZywKICAgICAgICAgICAgICAgc3BwID0gdGF4YSkgJT4lIAogICAgICAgIHNlbGVjdChpbmRleCwgc3RhdGUsIHllYXIsIHJlZ2lvbiwgc3BwLCBsb249eCwgbGF0PXksIHZhbHVlID0geikKICAgICAgCiAgICB9CiAgIAogICAgcmV0dXJuKGZpdF90aW4pCn0KCiMgVGVzdCBtZQojIE5lZWRzIHZhcmlhYmxlcyBpbiBDb250cm9sIHBhbmVsCiMgVGVzdCBubyBkYXRhOiAiQWxvc2EgYWVzdGl2YWxpcyIsIHJlZyA9ICJOb3J0aGVhc3QgVVMgRmFsbCIsIHlyID0gMTk3NCAKIyB0aXMoaW5wdXRfZGF0YSA9IG9jZWFuX2RhdGEsIGdyaWQgPSBncmlkX2Vlel9kZiwgdGF4YSA9ICJJbGxleCBpbGxlY2Vicm9zdXMiLCByZWcgPSAiTm9ydGhlYXN0IFVTIEZhbGwiLCB5ciA9IDE5NzMpCgoKCiMgbGFwcGx5KHllYXJzLHRpcyxpbnB1dF9kYXRhID0gb2NlYW5fZGF0YSwgZ3JpZCA9IGdyaWRfZWV6X2RmLCB0YXhhID0gIklsbGV4IGlsbGVjZWJyb3N1cyIsIHJlZyA9IHJlZ2lvbnNbMl0pCgpgYGAKCiMjIyMjIFJ1biBmdW5jdGlvbgoKVGhpcyBpcyBhIHN1Yi1mdW5jdGlvbiB0aGF0IHJ1bnMgdGhlIGB0aXMoKWAgZnVuY3Rpb24gYnkgdGF4YSBhbmQgcmVnaW9uLiBJdCBzYXZlcyB0aGUgb3V0cHV0IGFzIGEgLmNzdiBmaWxlIGZvciBlYWNoIHNwZWNpZXMuIAoKYGBge3IgaW50ZXJwb2xfcnVuX2Z1biwgZXZhbCA9IFQsIGVjaG8gPSBUfQoKCnJ1bl90aXMgPC0gZnVuY3Rpb24oaW5wdXRfZGF0YSwgZ3JpZCwgeWVhcnMsIHRheGEsIHJlZyl7CiAgCiAgCiAgIyBSdW4gdGlzIGZvciBzcGVjaWVzIGFuZCBzdXJ2ZXlzCiAgZm9yKHIgaW4gMToyKXsKICAgIAogICAgcGFydGlhbF9kZiA8LSBiaW5kX3Jvd3MoCiAgICAgIGxhcHBseSh5ZWFycyx0aXMsaW5wdXRfZGF0YSA9IG9jZWFuX2RhdGEsIGdyaWQgPSBncmlkX2Vlel9kZiwgdGF4YSA9IHRheGEsIHJlZyA9IHJlZ2lvbnNbcl0pCiAgICApCiAgICAKICAgIGlmKHIgPT0gMSl7CiAgICAgIGhpc3RvcmljX3RpZiA8LSBwYXJ0aWFsX2RmCiAgICB9ZWxzZXsKICAgICAgaGlzdG9yaWNfdGlmIDwtIGJpbmRfcm93cyhoaXN0b3JpY190aWYscGFydGlhbF9kZikKICAgIH0KICAgIAogIH0KICAKICAjIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tICMKICAjIFNhdmUgZGF0YXNldCBwZXIgc3BlY2llcwogICMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gIwogIAogICMgU2V0IGZpbGUgbmFtZQogIG5hbWUgPC0gcGFzdGUwKCJ0aWZfIixzdHJfcmVwbGFjZSh0YXhhLCIgIiwiXyIpLCIuY3N2IikKICAKICAjIFNldCBwYXRoIG5hbWUKICBzYXZlX3BhdGggPC0gbXlfcGF0aCgiUiIsIlBhcnRpYWwvSW50ZXJwb2xhdGlvbiIpCiAgCiAgIyBTZXQgY29tcGxldGUgcGF0aAogIHNhdmVfbmFtZSA8LSBwYXN0ZTAoc2F2ZV9wYXRoLG5hbWUpCiAgCiAgIyBDcmVhdGUgZm9sZGVyIGlmIGl0IGRvZXMgbm90IGV4aXN0CiAgaWYoZmlsZS5leGlzdHMoc2F2ZV9wYXRoKSA9PSBGKXsKICAgIGRpci5jcmVhdGUoc2F2ZV9wYXRoKQogIH0KICAKICAjICBTYXZlIGZpbGUKICB3cml0ZV9jc3YoaGlzdG9yaWNfdGlmLHNhdmVfbmFtZSkKICAKICByZXR1cm5fbXNnIDwtIHBhc3RlKCJJbnRlcnBvbGF0aW9uIGRvbmUgZm9yIiwgdGF4YSkKICAKICByZXR1cm4ocmV0dXJuX21zZykKICAKICAKfQoKIyBUZXN0IG1lCiAgICAjIHJ1bl90aXMoaW5wdXRfZGF0YSA9IG9jZWFuX2RhdGEsIGdyaWQgPSBncmlkX2Vlel9kZiwgdGF4YSA9ICJDZW50cm9wcmlzdGlzIHN0cmlhdGEiLCB5ZWFycyA9IHllYXJzLCByZWcgPSByZWdpb25zKQoKYGBgCgoKIyMjIyBTdXJ2ZXkgZGF0YQoKVGhlIGludGVycG9sYXRpb24gd2FzIGRvbmUgd2l0aCBOT0FBIE5vcnRoZWFzdCBGaXNoZXJpZXMgU2NpZW5jZSBDZW50ZXIgU3ByaW5nIGFuZCBGYWxsIEJvdHRvbSBUcmF3bCBTdXJ2ZXlzIFtkYXRhXShodHRwczovL3d3dy5maXNoZXJpZXMubm9hYS5nb3YvcmVnaW9uL25ldy1lbmdsYW5kLW1pZC1hdGxhbnRpYyNzY2llbmNlKSBwcm92aWRlZCBieSBbT2NlYW4gYWRhcHRdKGh0dHBzOi8vb2NlYW5hZGFwdC5ydXRnZXJzLmVkdS8pLiBEYXRhIHdhcyBhY2Nlc3NlZCB0cm91Z2ggdGhlIFtHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9waW5za3lsYWIvT2NlYW5BZGFwdCkuCgoqSW4gcHJpbWFyeSBwdWJsaWNhdGlvbnMgdXNpbmcgZGF0YSBmcm9tIHRoZSBkYXRhYmFzZSwgcGxlYXNlIGNpdGUgUGluc2t5IGV0IGFsLiAyMDEzLiBNYXJpbmUgdGF4YSB0cmFjayBsb2NhbCBjbGltYXRlIHZlbG9jaXRpZXMuIFNjaWVuY2UgMzQxOiAxMjM5LTEyNDIgZG9pOiAxMC4xMTI2L3NjaWVuY2UuMTIzOTM1MiwgYXMgd2VsbCBhcyB0aGUgb3JpZ2luYWwgZGF0YSBzb3VyY2VzLioKCi0gVXNpbmcgb25seSB0aGUgTm9ydGhlYXN0IFVTIEZhbGwgYW5kIFNwcmluZyBib3R0b20gdHJhd2wgc3VydmV5IGRhdGEgZm9yIG5vdwoKIyMjIyMgU3BsaXR0aW5nIHVwIGRhdGEKCi0gTm8gcGFydCBvbiBzcGF0aWFsIGFuYWx5c2lzLiBDYW4gYmUgaWdub3JlZC4gCgpUaGlzIGlzIGp1c3QgYSBzdWItc3RlcCB0byBzcGxpdCB1cCB0aGUgZGF0YSBpbnRvIHNpbmdsZSBzcGVjaWVzIGZpbGVzLiBUaGlzIG1ha2VzIHRoZSBhcHAgZmFzdGVyIGFzIGl0IG9ubHkgbmVlZHMgdG8gbG9hZCBzcGVjaWVzIHNwZWNpZmljIGRhdGEsIHJhdGhlciB0aGFuIGFsbCB0aGUgZGF0YSBhdCBkZSBiZWdpbmluZy4gCgpgYGB7ciBkYXRfZXhwbG9kZWQsIGV2YWwgPSBULCBlY2hvID0gRiwgZmlnLndpZHRoID0gOX0KCm9jZWFuX2RhdGEgPC0gcmVhZFJEUygiL1ZvbHVtZXMvRW50ZXJwcmlzZS9EYXRhL3BpbnNreWxhYi1PY2VhbkFkYXB0LTk2NmFkZjAvZGF0YV9jbGVhbi9kYXRfZXhwbG9kZWQucmRzIikgIyU+JSAKICAjIGZpbHRlcihzcHAgPT0gIkNlbnRyb3ByaXN0aXMgc3RyaWF0YSIsCiAgICAgICAjIHJlZ2lvbiAlaW4lIGMoIk5vcnRoZWFzdCBVUyBGYWxsIiAsICJOb3J0aGVhc3QgVVMgU3ByaW5nIikpICNObyBtb3JlIHNlYXNvbnMKCgpzcHBfZGF0YSA8LSBmdW5jdGlvbihzcHApewogIAogIG5hbWVfc2F2ZSA8LSBwYXN0ZTAobXlfcGF0aCgiRCIsIlNwcC9PYnNlcnZhdGlvbiIpLCJvYnNfIixzdHJfcmVwbGFjZShzcHBbMV0sICIgIiwgIl8iKSwiLmNzdiIpCgpvY2Vhbl9kYXRhICU+JSAKICBmaWx0ZXIoc3BwID09IHNwcCwKICAgICAgIHJlZ2lvbiAlaW4lIGMoIk5vcnRoZWFzdCBVUyBGYWxsIiAsICJOb3J0aGVhc3QgVVMgU3ByaW5nIikKICAgICAgICkgJT4lIAogIHdyaXRlX2NzdiguLCBuYW1lX3NhdmUpCiAgCn0KCnNwcF9saXN0IDwtIG9jZWFuX2RhdGEgJT4lIAogIGZpbHRlcihyZWdpb24gJWluJSByZWdpb25zLAogICAgICAgICBzcHAgIT0gIk5BIikgJT4lIAogIHB1bGwoc3BwKSAlPiUgCiAgdW5pcXVlKCkKICAKCmxhcHBseShzcHBfbGlzdCwgc3BwX2RhdGEpCgoKc3Vic2V0X2RhdGEgPC0gb2NlYW5fZGF0YSAlPiUgCiAgZmlsdGVyKHNwcCA9PSAiQ2VudHJvcHJpc3RpcyBzdHJpYXRhIiwKICAgICAgIHJlZ2lvbiAlaW4lIGMoIk5vcnRoZWFzdCBVUyBGYWxsIiAsICJOb3J0aGVhc3QgVVMgU3ByaW5nIikKICAgICAgICkKCmdncGxvdCgpICsKICBnZW9tX3BvaW50KGRhdGEgPSBzdWJzZXQoc3Vic2V0X2RhdGEsIHd0Y3B1ZSA9IDAuMCksCiAgICAgICAgICAgICBhZXMoCiAgICAgICAgICAgICAgIHggPSBsb24sCiAgICAgICAgICAgICAgIHkgPSBsYXQKICAgICAgICAgICAgICksCiAgICAgICAgICAgICBjb2xvciA9ICJncmV5OTUiCiAgKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3Vic2V0KHN1YnNldF9kYXRhLCB3dGNwdWUgPiAwKSwKICAgICAgICAgICAgIGFlcygKICAgICAgICAgICAgICAgeCA9IGxvbiwKICAgICAgICAgICAgICAgeSA9IGxhdCwKICAgICAgICAgICAgICAgY29sb3IgPSBsb2cxMCh3dGNwdWUpCiAgICAgICAgICAgICApLAogICAgICAgICAgICAgc2l6ZSA9IDEKICApICsKICBzY2FsZV9jb2xvcl9kaXN0aWxsZXIocGFsZXR0ZSA9ICJTcGVjdHJhbCIsIAogICAgICAgICAgICAgICAgICAgICAgICBndWlkZV9sZWdlbmQodGl0bGUgPSAiV0NQVUUgcGVyIEhhdWwgKGxvZzEwKSIpKSArIAogIGNvb3JkX3NmKHhsaW0gPSBjKC03NiwgLTY1KSx5bGltID0gYygzNSwgNDUpKSArCiAgTXlGdW5jdGlvbnM6Om15X2dndGhlbWVfbSgpICsKICBmYWNldF93cmFwKH5yZWdpb24pCgpgYGAKCiMjIyMgQ29udHJvbCBQYW5uZWwKClRoaXMgaXMgd2hlcmUgd2UgbG9hZCB0aGUgcmVxdWlyZWQgZGF0YSBhbmQgcHJlcGFyZSB0byBydW4gdGhlIGludGVycG9sYXRpb24gZnVuY3Rpb24uIE5vdGUgdGhhdCBzb21lIG9mIHRoZSBkYXRhIGhlcmUgaGFzIGJlZW4gcHJldmlvdXNseSBjcmVhdGVkIAoKYGBge3IgY29udHJvX3Bhbm5lbCwgZXZhbCA9IFQsIGVjaG8gPSBUfQoKIyBMb2FkIGdyaWQgZGYKZ3JpZF9lZXpfZGYgPC0gbXlfcGF0aCgiRCIsIlNwYXRpYWwiLCJncmlkX2Vlel9kZi5jc3YiLCByZWFkID0gVCkKCiMgUnVuIGludGVycG9sYXRpb24gZm9yIGFsbCB5ZWFycwp5ZWFycyA8LSBzZXEoMTk3MywyMDE5LDEpCgojIHJlZ2lvbnMKcmVnaW9ucyA8LSBjKCJOb3J0aGVhc3QgVVMgRmFsbCIgLCAiTm9ydGhlYXN0IFVTIFNwcmluZyIpCgojIHNwZWNpZXMgbGlzdApzcHAgPC0gb2NlYW5fZGF0YSAlPiUgCiAgZmlsdGVyKHJlZ2lvbiAlaW4lIHJlZ2lvbnMsCiAgICAgICAgIHNwcCAhPSAiTkEiKSAlPiUgCiAgcHVsbChzcHApICU+JSAKICB1bmlxdWUoKQoKCiMgRG91YmxlIGNoZWNrIHJ1bnMKCnNwcF9ydW5zIDwtIHRpYmJsZSh0YXhhID0gKGxpc3QuZmlsZXMobXlfcGF0aCgiUiIsIlBhcnRpYWwvSW50ZXJwb2xhdGlvbiIpKSkpICU+JQogIG11dGF0ZSgKICAgIHRheGEgPSBzdHJfcmVtb3ZlKHRheGEsICJ0aWZfIiksCiAgICB0YXhhID0gc3RyX3JlbW92ZSh0YXhhLCAiLmNzdiIpLAogICAgdGF4YSA9IHN0cl9yZXBsYWNlKHRheGEsICJfIiwgIiAiKQogICkgCgojIE1pc3NpbmcgcnVucwpzcHAgPC0gdGliYmxlKHRheGE9c3BwKSAlPiUgCiAgYW50aV9qb2luKHNwcF9ydW5zKSAlPiUgCiAgcHVsbCh0YXhhKQoKCmBgYAoKIyMjIyBSdW4gcm91dGluZQoKSGVyZSB3ZSBqdXN0IHJ1biB0aGUgcm91dGluZSBmb3IgZWFjaCBvZiB0aGUgc3BlY2llcyBwcmVzZW50IGluIHRoZSBOb3J0aGVhc3QgVVMgRmFsbCBhbmQgU3ByaW5nIHN1cnZleXMgYmV0d2VlbiAxOTczIGFuZCAyMDE5LiAKCi0gTm90ZSB0aGVyZSBhcmUgNDMgaWRlbnRpZmllZCB0YXhhCi0gU29tZSB0YXhhIGRvIG5vdCBoYXZlIHByZXNlbmNlIGRhdGEgaW4gc29tZSB5ZWFycwoKYGBge3IgcnVuX3JvdXRpbmUsIGV2YWwgPSBULCBlY2hvID0gVH0KCiMgc2luZ2xlIHNwZWNpZXMgcnVuCiMgcnVuX3RpcyhpbnB1dF9kYXRhID0gb2NlYW5fZGF0YSwKIyAgICAgICAgIGdyaWQgPSBncmlkX2Vlel9kZiwKIyAgICAgICAgIHllYXJzID0geWVhcnMsCiMgICAgICAgICByZWcgPSByZWdpb25zLAojICAgICAgICAgdGF4YSA9ICJJbGxleCBpbGxlY2Vicm9zdXMiCiMgKQoKCiMgUnVuIHRoZW0gYWxsIGluIHBhcmFsbGVsCnBhcmFsbGVsOjptY2xhcHBseShzcHAsCiAgICAgICAgICAgICAgICAgICBydW5fdGlzLCAKICAgICAgICAgICAgICAgICAgIGlucHV0X2RhdGEgPSBvY2Vhbl9kYXRhLAogICAgICAgICAgICAgICAgICAgZ3JpZCA9IGdyaWRfZWV6X2RmLAogICAgICAgICAgICAgICAgICAgeWVhcnMgPSB5ZWFycywKICAgICAgICAgICAgICAgICAgIHJlZyA9IHJlZ2lvbnMKKQoKCmBgYAoKIyBSZXN1bHRzCgpUaGlzIHNlY3Rpb24gc2hvd3MgcHJlbGltaW5hcnkgcmVzdWx0cyBmb3IgQmxhY2sgc2VhIGJhc3MgKCpDZW50cm9wcmlzdGlzIHN0cmlhdGEqKS4KCmBgYHtyIGxvYWRfZGF0YSwgZXZhbCA9IFQsIGVjaG8gPSBUfQoKIyBMb2FkIGludGVycG9sYXRlZCBkYXRhCiMgIkNlbnRyb3ByaXN0aXMgc3RyaWF0YSIKIyAKaGlzdG9yaWNfdGlmIDwtIG15X3BhdGgoIlIiLCJQYXJ0aWFsL0ludGVycG9sYXRpb24iLCJ0aWZfQ2VudHJvcHJpc3Rpc19zdHJpYXRhLmNzdiIsIHJlYWQgPSBUKQoKIyBTcGF0aWFsIGRhdGEKClN0YXRlcyA8LSBjKCJtYWluZSIsICJuZXcgaGFtcHNoaXJlIiwgIm1hc3NhY2h1c2V0dHMiLCAiY29ubmVjdGljdXQiLCAicmhvZGUgaXNsYW5kIiwgIm5ldyB5b3JrIiwgIm5ldyBqZXJzZXkiLCAiZGVsYXdhcmUiLCAibWFyeWxhbmQiLCAidmlyZ2luaWEiLCAibm9ydGggY2Fyb2xpbmEiLCJwZW5uc3lsdmFuaWEiKSAKCiMgVVMgU3RhdGUgTWFwIChsYW5kKQoKbGFuZF9zZiA8LSBzdF9hc19zZihtYXAoInN0YXRlIiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpICU+JSAKICBmaWx0ZXIoSUQgJWluJSBTdGF0ZXMpIAoKCiMgUGVyaW9kcwpwZXJpb2RzIDwtdGliYmxlKAogIHBlcmlvZCA9IGMocmVwKCJhIGVhcmx5IiwxMiksCiAgICAgICAgICAgICByZXAoImIgbWlkIiwxMiksCiAgICAgICAgICAgICByZXAoImMgbGF0ZSIsMTIpLAogICAgICAgICAgICAgcmVwKCJkIG5vdyIsMTIpCiAgICAgICAgICAgICApLAogIHllYXIgPSBjKHNlcSgxOTcyLDE5ODQsMSksCiAgICAgICAgICAgIHNlcSgxOTg1LDE5OTcsMSksCiAgICAgICAgICAgIHNlcSgxOTk4LDIwMTEsMSksCiAgICAgICAgICAgIHNlcSgyMDEyLDIwMTksMSkKICAgICAgICAgICAgKQogICkKCiMgU3RhdGUgcGFsbGV0CgpzdGF0ZV9wYWxsZXQgPC0gYyh3ZXNfcGFsZXR0ZShuID0gNSwgbmFtZSA9ICJEYXJqZWVsaW5nMSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VzX3BhbGV0dGUobiA9IDUsIG5hbWUgPSAiRGFyamVlbGluZzIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlc19wYWxldHRlKG4gPSAzLCBuYW1lID0gIlJveWFsMSIpCiAgICAgICAgICAgICAgICAgICkKCgoKIyBwcmludCBmb3Igbm90ZWJvb2sKaGVhZChoaXN0b3JpY190aWYpCmBgYAoKCiMjIEF2ZXJhZ2UgcHJvcG9ydGlvbgoKVGhpcyBtYXAgc2hvd3MgdGhlIGF2ZXJhZ2UgcHJvcG9ydGlvbiBvZiB0aGUgZXh0cmFwb2xhdGVkIHZhbHVlIGZyb20gdGhlIHdob2xlIHN0dWR5IHBlcmlvZCB3aXRoaW4gZWFjaCBTdGF0ZSdzIHdhdGVyLgoKKk5vdGU6KiBHcmV5IHRpbGVzIHJlcHJlc2VudCBgTkFzYAoKYGBge3IgYXJlYV9tYXBfc3RhdGUsIGV2YWwgPSBULCBlY2hvID0gVCwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyfQoKIGRhdGFfZ3JpZCA8LSBoaXN0b3JpY190aWYgJT4lIAogICMgbGVmdF9qb2luKHBlcmlvZHMpICU+JSAKICAjIGdyb3VwX2J5KHN0YXRlLGxhdCxsb24scGVyaW9kKSAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUsbGF0LGxvbixyZWdpb24pICU+JSAKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4odmFsdWUsbmEucm09IFQpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUgCiAgcmVuYW1lKElEID0gc3RhdGUpCgpnZ3Bsb3QoZGF0YV9ncmlkKSArCiAgZ2VvbV90aWxlKAogICAgYWVzKAogICAgICB4ID0gbG9uLAogICAgICB5ID0gbGF0LAogICAgICBmaWxsID0gbWVhbiwKICAgICAgY29sID0gbWVhbgogICAgKQogICkgKwogIGZhY2V0X3dyYXAofiBJRCAgKyAgcmVnaW9uLAogICAgICAgICAgICAgbmNvbCA9IDQpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoIk1lYW4gUHJvcG9ydGlvbiIsIGFscGhhID0gMC44KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpcygiTWVhbiBQcm9wb3J0aW9uIiwgYWxwaGEgPSAwLjgpCmBgYAoKIyMgUHJvcG9ydGlvbiBDaGFuZ2UKCkhlcmUgd2Ugc2hvdyB0aGUgYXZlcmFnZSBwcm9wb3J0aW9uIG9mIHRoZSBpbnRlcnBvbGF0aW9uIGJ5IFN0YXRlIGFuZCB0aW1lIHBlcmlvZC4gVGltZSBwZXJpb2RzIHdlcmUgYXJiaXRyYXJ5IGRlZmluZWQgYXM7CgotIEVhcmx5OyAxOTcyIHRvIDE5ODQKLSBNaWQ7IDE5ODUgdG8gMTk5NwotIExhdGU7IDE5OTggdG8gMjAxMQotIE5vdzsgMjAxMiB0byAyMDE5CgoqTm90ZXM6KiBUbyBwYW5lbCByZXByZXNlbnRzIEZhbGwgc3VydmV5IGFuZCBib3R0b20gcGFuZWwgU3ByaW5nLiBUaGlzIGNvbXB1dGF0aW9uIGNvbnNpZGVycyB0aGUgT3ZlcmxhcHBpbmcgb2Ygc3RhdGUgd2F0ZXJzLgoKYGBge3IgYXJlYV9tYXAsIGV2YWwgPSBULCBlY2hvID0gVCwgZmlnLndpZHRoID0gMTB9Cgp0b3RhbF9maXRlZCA8LSBoaXN0b3JpY190aWYgJT4lIAogIGdyb3VwX2J5KHllYXIscmVnaW9uKSAlPiUgCiAgc3VtbWFyaXNlKHRvdGFsX3ZhbHVlID0gc3VtKHZhbHVlLG5hLnJtPVQpLC5ncm91cHMgPSAiZHJvcCIpCgpzdGF0ZV9maXQgPC0gaGlzdG9yaWNfdGlmICU+JSAKICBncm91cF9ieShzdGF0ZSx5ZWFyLHJlZ2lvbikgJT4lIAogIHN1bW1hcmlzZShzdGF0ZV92YWx1ZSA9IHN1bSh2YWx1ZSxuYS5ybT0gVCksIC5ncm91cHMgPSAiZHJvcCIpICU+JSAKICBsZWZ0X2pvaW4odG90YWxfZml0ZWQsCiAgICAgICAgICAgIGJ5ID0gYygieWVhciIsInJlZ2lvbiIpKSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IHN0YXRlX3ZhbHVlL3RvdGFsX3ZhbHVlKjEwMCkgJT4lIAogIGxlZnRfam9pbihwZXJpb2RzLAogICAgICAgICAgICBieSA9ICJ5ZWFyIikgJT4lIAogIGdyb3VwX2J5KElEID0gc3RhdGUscGVyaW9kLHJlZ2lvbikgJT4lIAogIHN1bW1hcmlzZShtZWFuX3BlciA9IHJvdW5kKG1lYW4ocGVyY2VudGFnZSkpLC5ncm91cHMgPSAiZHJvcCIpCiAgCiMgY2hlY2sgcGVyY2VudGFnZXMKIyBzdGF0ZV9maXQgJT4lIAojICAgZ3JvdXBfYnkocGVyaW9kKSAlPiUgCiMgICBzdW1tYXJpc2Uoc3VtKG1lYW5fcGVyKSkKCgpsYW5kX3NmICU+JSAKICBsZWZ0X2pvaW4oc3RhdGVfZml0LAogICAgICAgICAgICBieSA9ICJJRCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IG1lYW5fcGVyKSkgKwogIHZpcmlkaXM6OnNjYWxlX2ZpbGxfdmlyaWRpcygiTWVhbiBQcm9wb3J0aW9uIiwgYWxwaGEgPSAwLjgpICsKICBmYWNldF93cmFwKH5yZWdpb24gKyBwZXJpb2QsIG5yb3cgPSAyKSArCiAgbXlfZ2d0aGVtZV9tKCkKCmBgYAoKIyMgQXJlYSBwbG90CgpUaGlzIGdyYXBoIHNob3dzIHRoZSBwcm9wb3J0aW9uIG9mIHRoZSBpbnRlcnBvbGF0aW9uIHZhbHVlIGVhY2ggU3RhdGUgaGFzIG92ZXIgdGltZS4KCipOb3RlOiogVGhpcyBwbG90IGlzIGludGVyYWN0aXZlLiBGb3IgZWFzZSBjb21wYXJpc29uIGJldHdlZW4gU3RhdGVzIHlvdSBjYW47CgotICpPbmUqIGNsaWNrIG9uIGFueSBTdGF0ZSB0byAqcmVtb3ZlKiBpdCBmcm9tIHRoZSBwbG90IAotICpUd28qIGNsaWNrcyBvbiBhbnkgU3RhdGUgdG8gKmlzb2xhdGUgaXQqIGZyb20gdGhlIHBsb3QgKG90aGVyIHN0YXRlcyBjYW4gdGhlbiBiZSBhZGRlZCBieSBqdXN0IGNsaWNraW5nIG9uIHRoZW0pLgotIFRoZSBib3R0b20gcGFuZWwgYWxsb3dzIHlvdSB0byByZWR1Y2UgdGhlIHRpbWUgZnJhbWUKCmBgYHtyIGFyZWFfcGxvdCwgZXZhbCA9IFQsIGVjaG8gPSBULCBmaWcuaGVpZ2h0ID0gMTAsIGZpZy53aWR0aCA9IDEwfQoKdG90YWxfZml0ZWQgPC0gaGlzdG9yaWNfdGlmICU+JSAKICBncm91cF9ieSh5ZWFyLHJlZ2lvbikgJT4lIAogIHN1bW1hcmlzZSh0b3RhbF92YWx1ZSA9IHN1bSh2YWx1ZSxuYS5ybT1UKSwuZ3JvdXBzID0gImRyb3AiKQoKIyBncm91cCBieSBzdGF0ZQpzdGF0ZV9maXQgPC0gaGlzdG9yaWNfdGlmICU+JSAKICBncm91cF9ieShzdGF0ZSx5ZWFyLHJlZ2lvbikgJT4lIAogIHN1bW1hcmlzZShzdGF0ZV92YWx1ZSA9IHN1bSh2YWx1ZSxuYS5ybT0gVCksIC5ncm91cHMgPSAiZHJvcCIpICU+JSAKICBsZWZ0X2pvaW4odG90YWxfZml0ZWQsCiAgICAgICAgICAgIGJ5ID0gYygieWVhciIsInJlZ2lvbiIpKSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IHN0YXRlX3ZhbHVlL3RvdGFsX3ZhbHVlKjEwMCkKCnAgPC0gZ2dwbG90KHN0YXRlX2ZpdCkgKwogIGdlb21fYXJlYSgKICAgIGFlcygKICAgICAgeCA9IHllYXIsCiAgICAgIHkgPSByb3VuZChwZXJjZW50YWdlKSwKICAgICAgZmlsbCA9IHN0YXRlCiAgICApCiAgKSArCiAgeWxhYigiUGVyY2VudGFnZSAoJSkiKSArCiAgIyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBULCBhbHBoYSA9IDAuOCkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHN0YXRlX3BhbGxldCkgKwogIE15RnVuY3Rpb25zOjpteV9nZ3RoZW1lX3AoKSArCiAgZmFjZXRfd3JhcCh+cmVnaW9uLCBuY29sID0gMSkKCmdncGxvdGx5KHAsCiAgICAgICAgIGR5bmFtaWNUaWNrcyA9IFRSVUUpICU+JSAKICBsYXlvdXQoaG92ZXJtb2RlID0gIngiKSAlPiUKICByYW5nZXNsaWRlcigpCgpgYGAKCiMjIEFyZWEgcGxvdCAocnVubmluZyBtZWFuKQoKVGhpcyBncmFwaCBzaG93cyB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIFN0YXRlIHNtb290aGVkIG92ZXIgYSAqMTAgeWVhcnMgcnVubmluZyBtZWFuKiouIEl0IGFsbG93cyB0byBiZXR0ZXIgc2VlIGluY3JlYXNpbmcgYW5kIGRlY3JlYXNpbmcgdHJlbmRzLgoKKk5vdGU6KiBUaGlzIHBsb3QgaXMgYWxzbyBpbnRlcmFjdGl2ZSBhbmQgdGh1cywgZm9sbG93cyB0aGUgc2FtZSBvcHRpb25zIG9mIHRoZSBwcmV2aW91cyBvbmUuCgpgYGB7ciBhcmVhX2xtX3Bsb3QsIGV2YWwgPSBULCBlY2hvID0gVCwgZmlnLmhlaWdodCA9IDEwLCBmaWcud2lkdGggPSAxMH0KCiMgZ3JvdXAgYnkgc3RhdGUKc3RhdGVfZml0IDwtIGhpc3RvcmljX3RpZiAlPiUgCiAgZ3JvdXBfYnkoc3RhdGUseWVhcixyZWdpb24sLmdyb3VwcyA9ICJkcm9wIikgJT4lIAogIHN1bW1hcmlzZShzdGF0ZV92YWx1ZSA9IHN1bSh2YWx1ZSxuYS5ybT0gVCksIC5ncm91cHMgPSAiZHJvcCIpICU+JSAKICBsZWZ0X2pvaW4odG90YWxfZml0ZWQsCiAgICAgICAgICAgIGJ5ID0gYygieWVhciIsInJlZ2lvbiIpKSAlPiUKICBtdXRhdGUocGVyY2VudGFnZSA9IHN0YXRlX3ZhbHVlL3RvdGFsX3ZhbHVlKjEwMCkgJT4lIAogIGdyb3VwX2J5KHN0YXRlLHJlZ2lvbikgJT4lIAogIG11dGF0ZShSTWVhbiA9IHpvbzo6cm9sbG1lYW4oeCA9IHBlcmNlbnRhZ2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybT1UKQogICAgKQoKIyBQbG90IG1lCnAgPC0gZ2dwbG90KHN0YXRlX2ZpdCkgKwogIGdlb21fYXJlYSgKICAgIGFlcygKICAgICAgeCA9IHllYXIsCiAgICAgIHkgPSByb3VuZChSTWVhbiksCiAgICAgIGZpbGwgPSBzdGF0ZQogICAgKQogICkgKwogIHlsYWIoIjEwIHlycyBydW5uaW5nIGF2ZXJhZ2UgKCUpIikgKwogICMgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlID0gVCwgYWxwaGEgPSAwLjgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBzdGF0ZV9wYWxsZXQpICsKICBNeUZ1bmN0aW9uczo6bXlfZ2d0aGVtZV9wKCkgKwogIGZhY2V0X3dyYXAofnJlZ2lvbiwgbmNvbCA9IDEpCgpzdXBwcmVzc1dhcm5pbmdzKApnZ3Bsb3RseShwLAogICAgICAgICBkeW5hbWljVGlja3MgPSBUUlVFKSAlPiUgCiAgbGF5b3V0KGhvdmVybW9kZSA9ICJ4IikgJT4lIAogICMgYWRkX3RyYWNlKCkgJT4lIAogIHJhbmdlc2xpZGVyKCkKKQoKYGBgCg==